#ifndef _QANSIDECODER_H
#define _QANSIDECODER_H


#include "qansieditor.h"
#include <QtCore/QObject>
#include <QtCore/QVector>
#include <QtCore/QTextCodec>


/* ASCII control-code characters */
#define CHAR_NUL    0x00    /* ^@ */
#define CHAR_SOH    0x01    /* ^A */
#define CHAR_STX    0x02    /* ^B */
#define CHAR_ETX    0x03    /* ^C */
#define CHAR_EOT    0x04    /* ^D */
#define CHAR_ENQ    0x05    /* ^E */
#define CHAR_ACK    0x06    /* ^F */
#define CHAR_BEL    0x07    /* ^G */
#define CHAR_BS     0x08    /* ^H */
#define CHAR_HT     0x09    /* ^I */
#define CHAR_LF     0x0A    /* ^J */
#define CHAR_VT     0x0B    /* ^K */
#define CHAR_FF     0x0C    /* ^L */
#define CHAR_CR     0x0D    /* ^M */
#define CHAR_SO     0x0E    /* ^N */
#define CHAR_SI     0x0F    /* ^O */
#define CHAR_DLE    0x10    /* ^P */
#define CHAR_DC1    0x11    /* ^Q */
#define CHAR_DC2    0x12    /* ^R */
#define CHAR_DC3    0x13    /* ^S */
#define CHAR_DC4    0x14    /* ^T */
#define CHAR_NAK    0x15    /* ^U */
#define CHAR_ETB    0x16    /* ^V */
#define CHAR_SYN    0x17    /* ^W */
#define CHAR_CAN    0x18    /* ^X */
#define CHAR_EM     0x19    /* ^Y */
#define CHAR_SUB    0x1A    /* ^Z */
#define CHAR_ESC    0x1B    /* ^[ */
#define CHAR_FS     0x1C    /* ^\ */
#define CHAR_GS     0x1D    /* ^] */
#define CHAR_RS     0x1E    /* ^^ */
#define CHAR_US     0x1F    /* ^_ */

#define CHAR_NORMAL   -1    /* default is normal character */


namespace QAnsiEditor {


class QAnsiBuffer;


/**
 * This class is used to decode ANSI code
 * Internal decoded strings are in unicode.
 * Alternatives ESCs and line separators are automatically detected.
 *
 * @author gonwan (gonwan at gmail dot com)
 */
class QAnsiDecoder
{

public:
    /**
     * Constructor
     * @param pBuffer Buffer to store decoded string
     * @param pCodec Text codec for decoding
     */
    QAnsiDecoder(/*in*/ QAnsiBuffer *pBuffer, /*in*/ QTextCodec *pCodec);
    /**
     * Destructor
     */
    ~QAnsiDecoder();

    /**
     * Decode input string
     * You may call this function for mutiple times.
     * Since telnet receives data continously, this function maintain internal decode status.
     * @param pszData The input data
     * @param iLength The length of input data
     */
    void decode(/*in*/ const char *pszData, /*in*/ int iLength);

private:
    /**
     * Whether the character is printable
     * The character can be a single-byte or part of a multi-bytes character.
     * @param uch the character
     * @return true if printable, false otherwise
     */
    static bool isPrint(/*in*/ const uchar uch);

    /********************************************************************************
     *     Following are callback functions when decoding.                          *
     *     You may want to refer to the state machine code in .cpp file. :)         *
     ********************************************************************************/

    /** Bell */
    void bel();
    /** Backspace */
    void bs();
    /** Horizontal tab */
    void ht();
    /** Line feed */
    void lf();
    /** Vertical tab */
    void vt();
    /** Form feed */
    void ff();
    /** Carriage return */
    void cr();

    /** Set current color and effect */
    void setAttributes();

    /** Erase to line begin, erase to line end or erase whole line */
    void eraseLine();
    /** Erase to screen begin, erase to screen end or erase whole screen */
    void eraseScreen();

    /** Insert lines at the line of cursor position */
    void insertLine();
    /** Delete lines at the line of cursor position */
    void deleteLine();
    /** Move down cursor, can cause scroll */
    void index();
    /** Move up cursor, can cause scroll */
    void reverseIndex();

    /** Cursor up */
    void cursorUp();
    /** Cursor down */
    void cursorDown();
    /** Cursor forword */
    void cursorForward();
    /** Cursor backward */
    void cursorBackward();
    /** Set cursor position */
    void setCursor();
    /** Save cursor position */
    void saveCursor();
    /** Restore cursor position */
    void restoreCursor();

    /** Normal characters, or other control characters which is not supported */
    void normal();

    /** Digital parameter */
    void param();
    /** Move to next parameter */
    void paramNext();
    /** Reset all parameters */
    void paramReset();

private:
    /** Function pointer of callback function */
    typedef void (QAnsiDecoder::*FnAction)();

    /** State machine definition for decoding */
    typedef struct State_ {
        /** Current character */
        char ch;
        /** Callback function to invoke */
        FnAction pfnAction;
        /** Next state */
        const State_ *pNextState;
    } State;

    /** Normal states */
    const static State normalState[];
    /** States after ESC */
    const static State escapeState[];
    /** States after ESC[ */
    const static State bracketState[];

    /** Input data */
    const char *m_pszData;
    /** Length of input data */
    int m_iDataLength;
    /** Current index of decode process */
    int m_iDataIndex;
    /** Decoded ANSI representation */
    QAnsiBuffer *m_pBuffer;
    /** Text codec for decoding */
    QTextCodec *m_pCodec;
    /** Internal decoding state of multi-byte characters */
    QTextCodec::ConverterState *m_pInternalState;
    /** Decoding state of state machine */
    State *m_pCurrentState;
    /** Current parameter */
    int m_iCurrentParam;
    /** Current parameter vector */
    QVector<int> m_paramVector;
    /** Whether we are decoding a multi-byte character that has more than one attribute */
    bool m_bHackingAttribute;

};


}  // namespace QAnsiEditor


#endif  // _QANSIDECODER_H
